package com.learn.security.theory.use;

import com.learn.security.utils.asymmetric_encryption.RSAUtil;
import com.learn.security.utils.message_digest.MDUtil;
import org.apache.commons.codec.binary.Hex;
import org.springframework.web.bind.annotation.RequestMethod;

import java.math.BigInteger;
import java.util.*;

/**
 * 测试
 */
public class Test {

    private static final String username = "admin";

    private static final String password = "admin";

    private static final String appAccessKey = "4a77dba1-ca2e-4dad-8a03-a2ffdbe2207a";

    private static final String tenantName = "AL";

    private static final String name = "SHEV";

    private static String requestId;

    private static String rsaModulus = "171637393279726063975451845054789921644107739505757357988406546548529724829587861092188396003269138812928630940833669174849815480780133318630305243792936252294093437205657032742704182930528718685120633916578712054200273127088822145704496935649245372609877546100245395747155441715853436385419221514225655717427";

    private static String rsaPublicExponent = "65537";

    private static String secretKey = "24f268a9-0a5f-4f04-b33b-4a7e0051b6ab";

    private static String tenantId = "000011";

    private static String token;

    private static String longTicket = "20000110000030000000000000000001";

    private static String refreshToken = "SHEV";

    private static String timestamp;

    private static String method;

    private static RequestMethod requestMethod = RequestMethod.GET;

    public static void main(String[] args) throws Exception {

        // 2.2 测试，获取时间差
        long diff = getTimeDiff();

        // 2.1 测试。获取rsaModulus，rsaPublicExponent，secretKey
        test2_1(diff);

        // 2.3 测试。获取token,tenantId
        test2_3(diff);

        // 2.4 测试。获取longTicket,refreshToken,userType
        test2_4(diff);

        // 2.5 测试。获取异常
        test2_5(diff);

        // 2.6 测试。
        test2_6(diff, null);

        // 2.7 测试。
        test2_7(diff, 1, null, null);

        // 2.8 测试。
        test2_8(diff, 1, null, null, null, null);

        // 2.9 测试。
        test2_9(diff, "20000110000085683701516603762021", null);

        // 2.10 测试。
        test2_10(diff);

        // 2.11 测试。
        test2_11(diff);

        // 2.12 测试。
        test2_12(diff);

        // 2.13 测试。
        test2_13(diff);

        // 2.14 测试。
        test2_14(diff, null);

        // 2.15 测试。
        test2_15(diff);

        // 2.16 测试。
        test2_16(diff);

        // 2.17 测试。
        test2_17(diff, 1);

        // 2.18 测试。
        test2_18(diff, 1, 1, null);

        // 2.19 测试。
        test2_19(diff);

        // 2.20 测试。
        test2_20(diff);

        // 2.21 测试。
        test2_21(diff, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);

        // 2.22 测试。
        test2_21(diff, null, null, null, "20000110000084753591516603763733", null, null, null, null, null, null, null, null, null, null, null, null, null);

        // 2.25 测试。
        test2_25(diff, null, "20000110000310000321499158476253", "2.25");

        // 2.26 测试。
        test2_25(diff, "BASIC", "20000110000310000321499158476253", "2.26");

        // 2.27 测试。
        test2_25(diff, "SOLUTION", "20000110000310000321499158476253", "2.27");

        // 2.28 测试。
        test2_25(diff, "TAG", "20000110000310000321499158476253", "2.28");

        // 2.29 测试。
        test2_25(diff, "UPD", "20000110000310000321499158476253", "2.29");

        // 2.30 测试。
        test2_25(diff, "SIMILAR", "20000110000310000321499158476253", "2.30");


    }

    /**
     * 2.1 测试。获取rsaModulus，rsaPublicExponent，secretKey
     *
     * @param diff 时间差
     */
    private static void test2_1(long diff) throws Exception {
        long times = System.currentTimeMillis() + diff;
        method = "/application/getApplicationByAccessKey";
        Map<String, Object> params = new HashMap<>();
        // 设置请求时间
        setTimestamp(times);
        // 获取签名参数
        getSignParam(params);
        // 获取签名
        String sign = sign(params);
        // 获取url的参数列表
        params.put("sign", sign);
        String url = getBaseUrl(method, params);
        // 发送请求
        String result1 = get("2.1", url);
        //171637393279726063975451845054789921644107739505757357988406546548529724829587861092188396003269138812928630940833669174849815480780133318630305243792936252294093437205657032742704182930528718685120633916578712054200273127088822145704496935649245372609877546100245395747155441715853436385419221514225655717427
        rsaModulus = result1.substring(result1.indexOf("\"rsaModulus\":\"") + 14, result1.indexOf("\",\"rsaPublicExponent\""));
        //65537
        rsaPublicExponent = result1.substring(result1.indexOf("\"rsaPublicExponent\":\"") + 21, result1.indexOf("\",\"rsaPrivateExponent\""));
        //24f268a9-0a5f-4f04-b33b-4a7e0051b6ab
        secretKey = result1.substring(result1.indexOf("\"secretKey\":\"") + 13, result1.indexOf("\",\"enabled\""));
    }

    /**
     * 2.3 测试。获取token,tenantId
     *
     * @param diff 时间差
     */
    private static void test2_3(long diff) throws Exception {
        long times = System.currentTimeMillis() + diff;
        method = "/user/authenticate";
        Map<String, Object> params = new HashMap<>();
        // 设置请求时间
        setTimestamp(times);
        // 获取签名参数
        getSignParam(params);
        // 获取签名
        String sign = sign(params);

        String encodePassword = password();
//        System.err.println("密码RSA加密后为：" + password);

        // 获取url的参数列表
        params.put("sign", sign);
        params.put("tenantName", tenantName);
        params.put("name", name);
        params.put("username", username);
        params.put("password", encodePassword);

        String url = getBaseUrl(method, params);
        // 发送请求
        String result1 = get("2.3", url);
        token = result1.substring(result1.indexOf("\"token\":\"") + 9, result1.indexOf("\"}],\"pageNumber\""));
        // 000011
        tenantId = result1.substring(result1.indexOf("\"tenantId\":\"") + 12, result1.indexOf("\",\"serverTime\""));
    }

    /**
     * 2.4 测试。获取longTicket,refreshToken,userType
     *
     * @param diff 时间差
     */
    private static void test2_4(long diff) throws Exception {
        long times = System.currentTimeMillis() + diff;
        method = "/user/authorize";
        Map<String, Object> params = new HashMap<>();
        // 设置请求时间
        setTimestamp(times);
        // 获取签名参数
        getSignParam(params);
        // 获取签名
        String sign = sign(params);

        // 获取url的参数列表
        params.put("sign", sign);
        params.put("tenantId", tenantId);
        params.put("name", name);
        params.put("token", token);
        String url = getBaseUrl(method, params);
        // 发送请求
        String result1 = get("2.4", url);
        // 20000110000030000000000000000001
        longTicket = result1.substring(result1.indexOf("\"longTicket\":\"") + 14, result1.indexOf("\",\"ticketDateThru\""));
        refreshToken = result1.substring(result1.indexOf("\"refreshToken\":\"") + 16, result1.indexOf("\"}],\"pageNumber\""));
    }

    /**
     * 2.5 测试。
     *
     * @param diff 时间差
     */
    private static void test2_5(long diff) throws Exception {
        long times = System.currentTimeMillis() + diff;
        method = "/errormsg/getSystemErrorMessageMapping";
        // 设置请求时间
        setTimestamp(times);

        Map<String, Object> params = new HashMap<>();

        // 获取签名参数
        getSignParam(params);
        // 获取签名
        String sign = sign(params);

        // 获取url的参数列表
        params.put("sign", sign);
        String url = getBaseUrl(method, params);
        // 发送请求
        get("2.5", url);
    }

    /**
     * 2.6-- 测试基础
     *
     * @param diff 时间差
     */
    private static void testBase(long diff, Map<String, Object> addParams, String index) throws Exception {
        long times = System.currentTimeMillis() + diff;
        // 设置请求时间
        setTimestamp(times);

        Map<String, Object> params = new HashMap<>();

        // 获取签名基本参数
        getSignParam6(params);
        // 添加其他参数
        if (null != addParams) {
            params.putAll(addParams);
        }
        // 获取签名
        String sign = sign(params);

        // 获取url的参数列表
        params.put("sign", sign);
        String url = getBaseUrl(method, params);
        // 发送请求
        get(index, url);
    }

    /**
     * 2.6 测试。
     *
     * @param diff 时间差
     */
    private static void test2_6(long diff, Integer departmentId) throws Exception {
        method = "/subject/getSubjects";
        Map<String, Object> params = new HashMap<>();
        if (null != departmentId) {
            params.put("departmentId", departmentId);
        }
        testBase(diff, params, "2.6");
    }

    /**
     * 2.7 测试。
     *
     * @param diff               时间差
     * @param subjectId          学科
     * @param educationalStageId 学段
     * @param name               教材版本名字
     */
    private static void test2_7(long diff, Integer subjectId, Integer educationalStageId, String name) throws Exception {
        method = "/textbook/getTextbookVersion";

        Map<String, Object> params = new HashMap<>();
        if (null != subjectId) {
            params.put("subjectId", subjectId);
        }
        if (null != educationalStageId) {
            params.put("educationalStageId", educationalStageId);
        }
        if (null != name) {
            params.put("name", name);
        }
        testBase(diff, params, "2.7");
    }

    /**
     * 2.8 测试。
     *
     * @param diff              时间差
     * @param subjectId         学科
     * @param gradeId           年级
     * @param volume            册
     * @param textbookVersionId 教材版本 ID
     * @param name              教材名字
     */
    private static void test2_8(long diff, Integer subjectId, Integer gradeId, Integer volume,
                                String textbookVersionId, String name) throws Exception {
        method = "/textbook/getTextbook";
        Map<String, Object> params = new HashMap<>();
        if (null != subjectId) {
            params.put("subjectId", subjectId);
        }
        if (null != gradeId) {
            params.put("gradeId", gradeId);
        }
        if (null != volume) {
            params.put("volume", volume);
        }
        if (null != textbookVersionId) {
            params.put("textbookVersionId", textbookVersionId);
        }
        if (null != name) {
            params.put("name", name);
        }
        testBase(diff, params, "2.8");
    }

    /**
     * 2.9 测试。
     *
     * @param diff       时间差
     * @param textbookId 教材 Id
     * @param version    当前客户端版本号
     */
    private static void test2_9(long diff, String textbookId, Long version) throws Exception {
        method = "/textbook/getChapterResource";
        Map<String, Object> params = new HashMap<>();
        if (null != textbookId) {
            params.put("textbookId", textbookId);
        }
        if (null != version) {
            params.put("version", version);
        }
        testBase(diff, params, "2.9");
    }

    /**
     * 2.10 测试。
     *
     * @param diff 时间差
     */
    private static void test2_10(long diff) throws Exception {
        method = "/meta/getFormType";
        testBase(diff, null, "2.10");
    }

    /**
     * 2.11 测试。
     *
     * @param diff 时间差
     */
    private static void test2_11(long diff) throws Exception {
        method = "/meta/getSemanticType";
        testBase(diff, null, "2.11");
    }

    /**
     * 2.12 测试。
     *
     * @param diff 时间差
     */
    private static void test2_12(long diff) throws Exception {
        method = "/meta/getKpDifficulty";
        testBase(diff, null, "2.12");
    }

    /**
     * 2.13 测试。
     *
     * @param diff 时间差
     */
    private static void test2_13(long diff) throws Exception {
        method = "/meta/getEducationalStage";
        testBase(diff, null, "2.13");
    }

    /**
     * 2.14 测试。
     *
     * @param diff 时间差
     */
    private static void test2_14(long diff, Integer id) throws Exception {
        method = "/meta/getRegions";
        Map<String, Object> params = new HashMap<>();
        if (null != id) {
            params.put("id", id);
        }
        testBase(diff, params, "2.14");
    }

    /**
     * 2.15 测试。
     *
     * @param diff 时间差
     */
    private static void test2_15(long diff) throws Exception {
        method = "/meta/getSignificance";
        testBase(diff, null, "2.15");
    }

    /**
     * 2.16 测试。
     *
     * @param diff 时间差
     */
    private static void test2_16(long diff) throws Exception {
        method = "/exercise/getExerciseAbilityTypes";
        testBase(diff, null, "2.16");
    }

    /**
     * 2.17 测试。
     *
     * @param diff 时间差
     * @param type 能力值类型
     */
    private static void test2_17(long diff, Integer type) throws Exception {
        method = "/exercise/getExerciseAbilitys";
        Map<String, Object> params = new HashMap<>();
        if (null != type) {
            params.put("type", type);
        }
        testBase(diff, params, "2.17");
    }

    /**
     * 2.18 测试。
     *
     * @param diff               时间差
     * @param subjectId          学科 id
     * @param educationalStageId 学段 id
     * @param version            时间差 当前客户端版本号
     */
    private static void test2_18(long diff, Integer subjectId, Integer educationalStageId, Long version) throws Exception {
        method = "/kp/getKnowledgePointResource";
        Map<String, Object> params = new HashMap<>();
        if (null != subjectId) {
            params.put("subjectId", subjectId);
        }
        if (null != educationalStageId) {
            params.put("educationalStageId", educationalStageId);
        }
        if (null != version) {
            params.put("version", version);
        }
        testBase(diff, params, "2.18");
    }

    /**
     * 2.19 测试。
     *
     * @param diff 时间差
     */
    private static void test2_19(long diff) throws Exception {
        method = "/meta/getErrorMetas";
        testBase(diff, null, "2.19");
    }

    /**
     * 2.20 测试。
     *
     * @param diff 时间差
     */
    private static void test2_20(long diff) throws Exception {
        method = "/meta/getCompleteness";
        testBase(diff, null, "2.20");
    }

    /**
     * 2.21 测试。
     *
     * @param diff 时间差
     */
    private static void test2_21(long diff, String strategy, String text, String kp, String section, String ability,
                                 String formType, String semanticType, String subject, String significance,
                                 String educationalStageId, String region, String year, String difficulty,
                                 String completeness, String errorMeta, Integer pageNumber, Integer pageSize) throws Exception {
        method = "/search/searchExercise";
        Map<String, Object> params = new HashMap<>();
        if (null != strategy) {
            params.put("strategy", strategy);
        }
        if (null != text) {
            params.put("text", text);
        }
        if (null != kp) {
            params.put("kp", kp);
        }
        if (null != section) {
            params.put("section", section);
        }
        if (null != ability) {
            params.put("ability", ability);
        }
        if (null != formType) {
            params.put("formType", formType);
        }
        if (null != semanticType) {
            params.put("semanticType", semanticType);
        }
        if (null != subject) {
            params.put("subject", subject);
        }
        if (null != significance) {
            params.put("significance", significance);
        }
        if (null != educationalStageId) {
            params.put("educationalStageId", educationalStageId);
        }
        if (null != region) {
            params.put("region", region);
        }
        if (null != year) {
            params.put("year", year);
        }
        if (null != difficulty) {
            params.put("difficulty", difficulty);
        }
        if (null != completeness) {
            params.put("completeness", completeness);
        }
        if (null != errorMeta) {
            params.put("errorMeta", errorMeta);
        }
        if (null != pageNumber) {
            params.put("pageNumber", pageNumber);
        }
        if (null != pageSize) {
            params.put("pageSize", pageSize);
        }
        testBase(diff, params, "2.21");
    }

    /**
     * 2.25 测试。
     *
     * @param diff  时间差
     * @param type  请求类型
     * @param ids   题目 ID
     * @param index 测试数
     */
    private static void test2_25(long diff, String type, String ids, String index) throws Exception {
        method = "/exercise/getExerciseDetail";
        Map<String, Object> params = new HashMap<>();
        if (null != type) {
            params.put("type", type);
        }
        if (null != ids) {
            params.put("ids", ids);
        }
        testBase(diff, params, index);
    }

    /**
     * 获取时间差
     * 同时测试2.2
     */
    private static long getTimeDiff() throws Exception {
        long times = System.currentTimeMillis();
        System.out.println("发送请求的时间：" + times);
        String url = getBaseUrl("/system/getSystemTime", null);
        String object = get("2.2", url);
        object = object.substring(object.indexOf("\"values\":[") + 10, object.indexOf("],\"pageNumber\""));
        long time = Long.parseLong(object);

        return time - times;
    }

    private static String get(String index, String url) throws Exception {
        String result = RestTemplateUtil.get(url);
        System.out.println(index + "的请求路径为：" + url);
        System.out.println(index + "的结果为：" + result);
        return result;
    }

    /**
     * 设置请求时间
     *
     * @param times 时间戳
     */
    private static void setTimestamp(long times) {
        timestamp = times + "";
        System.out.println("发送请求的时间：" + timestamp);
    }


    /**
     * 密码RSA加密
     */
    private static String password() {

        BigInteger modulus = new BigInteger(rsaModulus);
        BigInteger publicExponent = new BigInteger(rsaPublicExponent);

        String publicKey = RSAUtil.getPublicKey(modulus.toByteArray(), publicExponent.toByteArray());

//        System.out.println("公钥为：" + publicKey);

        byte[] encoded = RSAUtil.encryptByPublicKey(password.getBytes(), publicKey);

        return Hex.encodeHexString(encoded);
    }

    /**
     * 获取签名
     *
     * @param params 需要加密的参数
     */
    private static String sign(Map<String, Object> params) {
        return generateSign(requestMethod, method, params, secretKey);
    }

    /**
     * 获取请求路径\
     *
     * @param path     请求路径
     * @param paramMap 参数的map
     */
    private static String getBaseUrl(String path, Map<String, Object> paramMap) {
        StringBuilder url = new StringBuilder();
        url.append("http://122.144.218.147/al")
                .append(path);

        if (null != paramMap) {
            boolean flag = true;
            for (Map.Entry<String, Object> entry : paramMap.entrySet()) {

                if (entry.getKey() != null) {
                    if (flag) {
                        url.append("?").append(entry.getKey()).append("=").append(entry.getValue());
                        flag = false;
                    } else {
                        url.append("&").append(entry.getKey()).append("=").append(entry.getValue());
                    }
                }
            }
        }
        return url.toString();
    }

    /**
     * 2.1 需要加密的参数
     */
    private static void getSignParam(Map<String, Object> params) {
        requestId = String.valueOf((int) (1 + Math.random() * 1000));
        params.put("appAccessKey", appAccessKey);
        params.put("timestamp", timestamp);
        params.put("requestId", requestId);
    }

    /**
     * 2.6 需要加密的参数
     */
    private static void getSignParam6(Map<String, Object> params) {
        getSignParam(params);
        params.put("longTicket", longTicket);
    }

    /**
     * 求解答提供
     */
    private static String generateSign(RequestMethod requestMethod, String method, Map<String, Object> params, String secretKey) {
        List<Map.Entry<String, Object>> paramList = new ArrayList<>(params.entrySet());

        paramList.sort(Comparator.comparing(o -> (o.getKey())));

        StringBuffer paramStr = new StringBuffer();
        for (Map.Entry<String, Object> entry : paramList) {
            if (entry.getValue() != null)
                paramStr.append(entry.getKey()).append("=").append(entry.getValue());
        }

        try {
            String s = requestMethod.name() + method + paramStr + secretKey;
            String md5Str = Hex.encodeHexString(MDUtil.encryptMD5(s.getBytes()));
            return md5Str;
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }


}
